﻿using AutoMapper;
using Microsoft.Owin.Security;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;
using System.Web;
using System.Web.Http;
using System.Web.Mvc;
using System.Net.Http;
using LinqKit;
using Stackoverflow.Web.Results;
using System.Data.SqlClient;
using Stackoverflow.Core.Base;
using Stackoverflow.Web.Models;
using Microsoft.AspNet.Identity;

namespace Stackoverflow.Web.Controllers
{
    public abstract class BaseApiController<T, TModel, TSearchModel> : ApiController
        where T : Entity
        where TModel : BaseModel
        where TSearchModel : BaseModel
    {
        public IRepository<T> Repository { get; set; }

        private IAuthenticationManager AuthenticationManager
        {
            get
            {
                return HttpContext.Current.GetOwinContext().Authentication;
            }
        }

        public BaseApiController(IRepository<T> repository)
        {
            Repository = repository;
        }

        public virtual IHttpActionResult Get()
        {
            return QueryData();
        }

        public virtual IHttpActionResult GetBy(Guid id)
        {
            var entity = Repository.GetBy(id);
            var model = Mapper.Map<TModel>(entity);

            if (model == null)
            {
                return NotFound();
            }

            return Ok(model);
        }

        public virtual IHttpActionResult Post(TModel model)
        {
            var entity = Mapper.Map<T>(model);

            entity.Id = Guid.NewGuid();

            entity.CreatedDate = DateTime.Now;

            try
            {
                Repository.Add(entity);
            }
            catch (Exception e)
            {
                return InternalServerError(e);
            }

            return Ok(entity.Id);
        }

        public virtual IHttpActionResult Put(TModel model)
        {
            var entity = Mapper.Map<T>(model);

            entity.UpdatedDate = DateTime.Now;

            try
            {
                Repository.Update(entity);
            }
            catch (Exception e)
            {
                return InternalServerError(e);
            }

            return Ok();
        }

        public virtual IHttpActionResult Delete(Guid id)
        {
            try
            {
                Repository.Remove(id);
            }
            catch (Exception e)
            {
                return InternalServerError(e);
            }

            return Ok();
        }

        protected int? TryParseInt(string value)
        {
            int? result = null;
            int tempData;
            if (int.TryParse(value, out tempData))
            {
                result = tempData;
            }

            return result;
        }

        protected IHttpActionResult QueryData(Expression<Func<T, bool>> filter = null, int maxItems = 10)
        {
            var queryItems = Request.RequestUri.ParseQueryString();
            int? page = TryParseInt(queryItems["page"]);
            int? count = TryParseInt(queryItems["count"]);

            int total;
            var entities = Repository.DbSet().AsExpandable().OrderByDescending(x => x.Id).AsQueryable();

            if (filter != null)
            {
                entities = entities.Where(filter);
                total = entities.Count();
            }
            else
            {
                total = entities.Count();
            }

            if (count.HasValue && page.HasValue)
            {
                entities = entities.Skip(count.Value * (page.Value - 1)).Take(count.Value);
            }

            var model = Mapper.Map<IEnumerable<TSearchModel>>(entities);
            return Request.CreateSearchResult<TSearchModel>(model, total);
        }

        protected IHttpActionResult QueryWithOrder(Expression<Func<T, bool>> filter = null, Expression<Func<T, bool>> order = null , SortOrder orderType = SortOrder.Ascending)
        {
            var queryItems = Request.RequestUri.ParseQueryString();
            int? page = TryParseInt(queryItems["page"]);
            int? count = TryParseInt(queryItems["count"]);

            int total;
            var entities = Repository.DbSet().AsExpandable().OrderBy(x => x.CreatedDate).AsExpandable();

            if (filter != null)
            {
                entities = entities.Where(filter);
                total = entities.Count();
            }
            else
            {
                total = entities.Count();
            }

            if(order != null)
            {
                if (orderType == SortOrder.Ascending)
                {
                    entities = entities.OrderBy(order);
                }
                else
                {
                    entities = entities.OrderByDescending(order);
                }
            }


            if (count.HasValue && page.HasValue)
            {
                entities = entities.Skip(count.Value * (page.Value - 1)).Take(count.Value);
            }

            var model = Mapper.Map<IEnumerable<TSearchModel>>(entities);

            return Request.CreateSearchResult<TSearchModel>(model, total);
        }

        protected IHttpActionResult ValidateData(Expression<Func<T, bool>> filter)
        {
            return Ok(new { Result = !Repository.DbSet().Any(filter) });
        }
    }
}